#ifndef __hash_h
#define __hash_h

/**
 * @file hash.h
 *
 * @brief Generic hash table. It is implemented as an array of doubly-linked
 * lists of iterators. The index within the array is computed by a efficient
 * hash function.
 */
#include <stddef.h>

__BEGIN_DECLS

struct __hash_ops {
    unsigned int (*hash)(const void *);

    int (*compare)(const void *, const void *);

    void *(*key_dup)(const void *);

    void (*key_free)(void *);

    void *(*data_dup)(const void *);

    void (*data_free)(void *);
};

/**
 * This structure holds hash table operations.
 */
typedef struct __hash_ops hash_ops_t;

/**
 * This is the hash table.
 */
typedef struct __hash hash_t;

struct __hash_iter {
    void *key;
    void *data;
    struct __hash *hash;
    unsigned int __hkey;
    struct __hash_iter *__next;
    struct __hash_iter *__prev;
    int __foreach;
};

/**
 * This is the elementary container for storing data into the hash table.
 */
typedef struct __hash_iter hash_iter_t;

/**
 * Signature of a "foreach" function.
 */
typedef unsigned int (*hash_func_t)(void *a_key, void *a_data, void *a_userdata);

/**
 * Creates a new hash table. One can customize the memory (de)allocation
 * policy for keys and data stored in the hash table.
 * @param a_size the initial size of the array.
 * @param a_ops the hash operations. If NULL, then string keys are assumed and
 * no memory (de)allocation is performed for keys and data.
 * @return a dynamicaly allocated hash table.
 */
extern hash_t *hash_new(size_t a_size, const hash_ops_t *a_ops);

/**
 * Destroys the hash table.
 */
extern void hash_delete(hash_t *a_this);

/**
 * clears the hash table.
 */
extern void hash_clear(hash_t *a_this);

/**
 * Looks for the iterator associated to the given key in the hash table.
 * @param a_key the key associated to the iterator.
 * @return a pointer to the found iterator or NULL.
 */
extern hash_iter_t *hash_lookup(hash_t *a_this, const void *a_key);

/**
 * Looks for the iterator associated to the given key in the hash table and
 * creates it if doesn't exist.
 * @param a_key the key associated to the iterator.
 * @return a pointer to the found iterator or NULL.
 */
extern hash_iter_t *hash_look_add(hash_t *a_this, const void *a_key);

/**
 * Adds data associated with the given key into the hash table. If the
 * key already exists, the old iterator is freed according to the memory
 * management operations passed to hash_new().
 * @param a_key the key associated to the iterator.
 * @return a pointer to the created or found iterator.
 */
extern hash_iter_t *hash_add(hash_t *a_this, const void *a_key, void *a_data);

/**
 * Removes an iterator from the hash table.
 * @param a_key the key associated to the iterator.
 */
extern void hash_del(hash_t *a_this, const void *a_key);

/**
 * Calls \a a_func for each element of the hash table, as long as \a a_func
 * returns 0.
 * @param a_func the "foreach" function.
 * @param a_data the user data passed to \a a_func.
 */
extern void hash_foreach(hash_t *a_this, hash_func_t a_func, void *a_data);

/**
 * Gets the number of iterators.
 */
extern unsigned int hash_get_nelem(hash_t *a_this);

/**
 * Gets the size of the array.
 */
extern unsigned int hash_get_size(hash_t *a_this);

/**
 * Gets the first iterator.
 */
extern hash_iter_t *hash_begin(hash_t *a_this);

/**
 * Gets the last iterator.
 */
extern hash_iter_t *hash_end(hash_t *a_this);

/**
 * Gets a pointer to the next iterator.
 */
extern hash_iter_t *hash_iter_next(hash_iter_t *a_this);

/**
 * Gets a pointer to the previous iterator.
 */
extern hash_iter_t *hash_iter_prev(hash_iter_t *a_this);

/**
 * Gets a pointer to the previous iterator.
 */
extern void hash_iter_del(hash_iter_t *a_this);

/**
 * Hashes strings.
 */
extern unsigned int hash_hash_string(const char *a_string);

__END_DECLS

#endif
